﻿#include "Notification.h"
#include <QPainter>
#include <QStyleOption>
#include <QGraphicsDropShadowEffect>
#include <QScrollBar>
#include <QPropertyAnimation>
#include <QApplication>
#include <QScreen>
#include <QEvent>
#include <QResizeEvent>
#include "Defs.h"

static int nAppearTime = 200; // 出现的时间200ms
static int nDisappearTime = 200; // 消失的时间200ms

Notification::Notification(QObject* parent) : QObject(parent)
{
    QWidget* pWidget = qobject_cast<QWidget*>(parent);
    if (pWidget == nullptr)
        throw std::runtime_error("parent of notification error!");

    m_vecItem.reserve(30);

    // 安装事件过滤器，监听父窗口的 resize 事件
    pWidget->installEventFilter(this);
}

Notification::~Notification()
{
    // 移除事件过滤器
    if (QWidget* pWidget = qobject_cast<QWidget*>(parent())) {
        pWidget->removeEventFilter(this);
    }
}

bool Notification::eventFilter(QObject* watched, QEvent* event)
{
    // 监听父窗口的 resize 事件
    if (event->type() == QEvent::Resize) {
        QWidget* pWidget = qobject_cast<QWidget*>(watched);
        if (pWidget && pWidget == parent()) {
            // 窗口大小改变时，重新布局所有通知
            relayoutItems();
        }
    }
    return QObject::eventFilter(watched, event);
}

void Notification::relayoutItems()
{
    std::lock_guard<std::mutex> lck(m_vecMtx);

    // 获取当前窗口大小
    QWidget* pWidget = qobject_cast<QWidget*>(parent());
    if (!pWidget) {
        return;
    }
    QSize currentSize = pWidget->size();

    // 按位置分组重新计算每个通知的位置
    for (NotifyPosition pos : {Pos_Top_Right, Pos_Top_Left, Pos_Bottom_Left, Pos_Bottom_Right}) {
        int currentHeight = 0;
        int currentX = 0;
        bool bFirst = true;

        for (NotificationItem* item : m_vecItem) {
            // 关键修改：跳过正在消失的通知
            if (item->GetPosType() == pos && item->IsAppearEnd() && !item->IsDisappearing()) {
                if (bFirst) {
                    // 第一个通知项的位置
                    if (pos == NotifyPosition::Pos_Top_Right) {
                        currentX = currentSize.width() - item->width() - nMargin;
                        currentHeight = nMargin;
                    }
                    else if (pos == NotifyPosition::Pos_Top_Left) {
                        currentX = nMargin;
                        currentHeight = nMargin;
                    }
                    else if (pos == NotifyPosition::Pos_Bottom_Left) {
                        currentX = nMargin;
                        currentHeight = currentSize.height() - nMargin - item->height();
                    }
                    else { // Pos_Bottom_Right
                        currentX = currentSize.width() - item->width() - nMargin;
                        currentHeight = currentSize.height() - nMargin - item->height();
                    }
                    bFirst = false;
                }

                // 使用动画平滑移动到新位置
                QPropertyAnimation* pAnimation = new QPropertyAnimation(item, "geometry", this);
                pAnimation->setDuration(150);  // 比通知出现/消失快一点
                pAnimation->setStartValue(item->geometry());
                pAnimation->setEndValue(QRect(currentX, currentHeight, item->width(), item->height()));
                pAnimation->setEasingCurve(QEasingCurve::OutQuad);
                pAnimation->start(QAbstractAnimation::DeleteWhenStopped);

                // 计算下一个通知项的位置
                if (pos == NotifyPosition::Pos_Top_Right || pos == NotifyPosition::Pos_Top_Left) {
                    currentHeight += (item->height() + nMargin);
                }
                else { // Bottom
                    currentHeight -= (item->height() + nMargin);
                }
            }
        }
    }
}

void Notification::Push(NotifyType type, NotifyPosition pos, QString title, QString content, int nLive)
{
    std::lock_guard<std::mutex> lck(m_vecMtx);

    // 获取父窗口（动态获取，确保是最新的）
    QWidget* pWidget = qobject_cast<QWidget*>(parent());
    if (!pWidget) {
        qWarning() << "Notification::Push - 父窗口无效";
        return;
    }

    // 每次 Push 时动态获取当前窗口大小
    QSize currentSize = pWidget->size();

    NotificationItem* pItem = new NotificationItem(pWidget,
        type,
        pos,
        title,
        content,
        nLive);
    connect(pItem, &NotificationItem::itemRemoved, this, &Notification::itemRemoved);

    int currentHeight = 0;
    int currentX = 0;

    if (pos == NotifyPosition::Pos_Top_Right)
    {
        currentX = currentSize.width();
        currentHeight = nMargin;
    }
    else if (pos == NotifyPosition::Pos_Top_Left)
    {
        currentX = -pItem->width();
        currentHeight = nMargin;
    }
    else if (pos == NotifyPosition::Pos_Bottom_Left)
    {
        currentX = -pItem->width();
        currentHeight = currentSize.height() - nMargin - pItem->height();
    }
    else  // Pos_Bottom_Right
    {
        currentX = currentSize.width();
        currentHeight = currentSize.height() - nMargin - pItem->height();
    }

    for_each(m_vecItem.begin(), m_vecItem.end(), [&](NotificationItem* item) {
        if (item->GetPosType() == pos)
        {
            if (pos == NotifyPosition::Pos_Top_Right)
            {
                currentHeight += (item->height() + nMargin);
            }
            else if (pos == NotifyPosition::Pos_Top_Left)
            {
                currentHeight += (item->height() + nMargin);
            }
            else if (pos == NotifyPosition::Pos_Bottom_Left)
            {
                currentHeight -= (item->height() + nMargin);
            }
            else  // Pos_Bottom_Right
            {
                currentHeight -= (item->height() + nMargin);
            }
        }
        });

    pItem->move(currentX, currentHeight);
    m_vecItem.emplace_back(pItem);
    pItem->Show();
}

void Notification::relayoutPosition(NotifyPosition pos)
{
    std::lock_guard<std::mutex> lck(m_vecMtx);

    QWidget* pWidget = qobject_cast<QWidget*>(parent());
    if (!pWidget) {
        return;
    }
    QSize currentSize = pWidget->size();

    int currentY = 0;
    int currentX = 0;
    bool bFirst = true;

    for (NotificationItem* item : m_vecItem)
    {
        // 关键：跳过正在消失的通知
        if (item->GetPosType() == pos && item->IsAppearEnd() && !item->IsDisappearing())
        {
            if (bFirst)
            {
                // 计算第一个通知的位置
                if (pos == NotifyPosition::Pos_Top_Right)
                {
                    currentX = currentSize.width() - item->width() - nMargin;
                    currentY = nMargin;
                }
                else if (pos == NotifyPosition::Pos_Top_Left)
                {
                    currentX = nMargin;
                    currentY = nMargin;
                }
                else if (pos == NotifyPosition::Pos_Bottom_Left)
                {
                    currentX = nMargin;
                    currentY = currentSize.height() - nMargin - item->height();
                }
                else  // Pos_Bottom_Right
                {
                    currentX = currentSize.width() - item->width() - nMargin;
                    currentY = currentSize.height() - nMargin - item->height();
                }
                bFirst = false;
            }

            // 使用动画平滑移动
            QPropertyAnimation* pAnimation = new QPropertyAnimation(item, "geometry", this);
            pAnimation->setDuration(150);  // 150ms
            pAnimation->setStartValue(item->geometry());
            pAnimation->setEndValue(QRect(currentX, currentY, item->width(), item->height()));
            pAnimation->setEasingCurve(QEasingCurve::OutQuad);
            pAnimation->start(QAbstractAnimation::DeleteWhenStopped);

            // 计算下一个位置
            if (pos == NotifyPosition::Pos_Top_Right || pos == NotifyPosition::Pos_Top_Left)
            {
                currentY += (item->height() + nMargin);
            }
            else  // Bottom
            {
                currentY -= (item->height() + nMargin);
            }
        }
    }
}

void Notification::itemRemoved(NotificationItem* pRemoved)
{
    std::unique_lock<std::mutex> lck(m_vecMtx);

    // 从列表中移除
    NotifyPosition pos = pRemoved->GetPosType();
    for (auto itr = m_vecItem.begin(); itr != m_vecItem.end();)
    {
        if (*itr == pRemoved)
        {
            m_vecItem.erase(itr);
            break;
        }
        else ++itr;
    }

    lck.unlock();  // 提前解锁

    // 延迟重新布局，避免频繁触发
    // 如果 70ms 内有多个通知消失，只执行最后一次布局
    if (!m_relayoutTimer) {
        m_relayoutTimer = new QTimer(this);
        m_relayoutTimer->setSingleShot(true);
        m_relayoutTimer->setInterval(70);  // 70ms 延迟

        connect(m_relayoutTimer, &QTimer::timeout, this, [this, pos]() {
            relayoutPosition(pos);
            });
    }

    // 重启定时器（会取消之前的定时器）
    m_relayoutTimer->stop();
    m_relayoutTimer->start();
}

///
NotificationItem::NotificationItem(QWidget* parent,
    NotifyType type,
    NotifyPosition pos,
    QString title,
    QString content,
    int nLife) : QWidget(parent),
    m_enPos(pos),
    m_bAppearEnd(false)
{
    setObjectName(QStringLiteral("notification_item"));
    QLabel* pTitle = new QLabel(title, this);
    pTitle->setObjectName(QStringLiteral("label_title"));
    NotificationLabel* pContent = new NotificationLabel(this, nFixedWidth - 10, content);
    QFont font;
    font.setPointSize(11);
    font.setFamily(QStringLiteral("Microsoft Yahei"));
    pContent->setFont(font);
    QPushButton* pClose = new QPushButton(this);
    pClose->setFixedSize(16, 16);
    pClose->setObjectName(QStringLiteral("btn_close"));
    pClose->setCursor(QCursor(Qt::PointingHandCursor));

    setStyleSheet(QStringLiteral("QWidget#notification_item{border:none;border-radius:8px;background-color:white;}"
        "QLabel#label_title{border:none;background-color:white;font-family:Microsoft Yahei;font-size:20px;font-weight:700;color:#303133;}"
        "QPushButton#btn_close{border:none;background-color:white;background-position:center;border-image:url(:/icons/关闭.png);}"
        "QPushButton:hover#btn_close{border-image:url(:/icons/关闭_hovered.png);}"));

    // 标题设置
    pTitle->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    QFontMetrics fontWidth(pTitle->font());
    QString elideNote = fontWidth.elidedText(pTitle->text(),
        Qt::ElideRight,
        120);
    pTitle->setText(elideNote);
    pTitle->setToolTip(title);
    pTitle->adjustSize();

    // 内容设置
    pContent->Adjust();

    // 布局
    if (type != NotifyType::Notify_Type_None)
    {
        QLabel* pIcon = new QLabel(this);
        pIcon->setStyleSheet(QStringLiteral("QLabel{border:none;background-color:white;}"));
        if (type == NotifyType::Notify_Type_Success)
        {
            pIcon->setPixmap(QPixmap(":/TempPaste/icons/成功.png"));
        }
        else if (type == NotifyType::Notify_Type_Error)
        {
            pIcon->setPixmap(QPixmap(":/TempPaste/icons/错误.png"));
        }
        else if (type == NotifyType::Notify_Type_Warning)
        {
            pIcon->setPixmap(QPixmap(":/TempPaste/icons/告警实心.png"));
        }
        else
        {
            pIcon->setPixmap(QPixmap(":/TempPaste/icons/信息.png"));
        }
        pIcon->adjustSize();
        setFixedSize(nFixedWidth + nLeftPadding + nRightPadding + pIcon->width() + 10,
            pContent->height() + pTitle->height() + 2 * nTopPadding + 5);
        pIcon->move(nLeftPadding, nTopPadding - std::abs(pIcon->height() - pTitle->height()) / 2 + 3);
        pTitle->move(pIcon->x() + pIcon->width() + 10, nTopPadding);
    }
    else
    {
        setFixedSize(nFixedWidth + nLeftPadding + nRightPadding,
            pContent->height() + pTitle->height() + 2 * nTopPadding + 5);
        pTitle->move(nLeftPadding, nTopPadding);
    }

    pContent->move(pTitle->x(), pTitle->y() + pTitle->height() + 5);
    pClose->move(width() - pClose->width() / 2 - nRightPadding, nTopPadding);

    QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect(this);
    effect->setBlurRadius(20);        // 阴影圆角的大小
    effect->setColor(Qt::gray);      //阴影的颜色
    effect->setOffset(0, 0);          //阴影的偏移量
    setGraphicsEffect(effect);

    connect(pClose, &QPushButton::clicked, this, [&]() {
        m_liftTimer.stop();
        Disappear();
        });

    connect(&m_liftTimer, &QTimer::timeout, this, [&]() {
        m_liftTimer.stop();
        Disappear();
        });
    m_nLifeTime = nLife;
    hide();
}

NotificationItem::~NotificationItem()
{

}

void NotificationItem::Show()
{
    show();
    Appear();
}

NotifyPosition NotificationItem::GetPosType() const
{
    return m_enPos;
}

bool NotificationItem::IsAppearEnd() const
{
    return m_bAppearEnd;
}

void NotificationItem::Appear()
{
    QPropertyAnimation* animation = new QPropertyAnimation(this, "geometry");
    animation->setDuration(nAppearTime);
    animation->setStartValue(QRect(pos().x(), pos().y(), width(), height()));
    if (m_enPos == NotifyPosition::Pos_Top_Right)
    {
        animation->setEndValue(QRect(pos().x() - width() - nMargin,
            pos().y(),
            width(),
            height()));
    }
    else if (m_enPos == NotifyPosition::Pos_Top_Left)
    {
        animation->setEndValue(QRect(pos().x() + width() + nMargin,
            pos().y(),
            width(),
            height()));
    }
    else if (m_enPos == NotifyPosition::Pos_Bottom_Left)
    {
        animation->setEndValue(QRect(pos().x() + width() + nMargin,
            pos().y(),
            width(),
            height()));
    }
    else  // Pos_Bottom_Right
    {
        animation->setEndValue(QRect(pos().x() - width() - nMargin,
            pos().y(),
            width(),
            height()));
    }
    // 添加缓动效果
    QEasingCurve curve;
    curve.setType(QEasingCurve::OutCubic);
    animation->setEasingCurve(curve);

    animation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);

    connect(animation, &QPropertyAnimation::finished, this, [&]() {
        m_bAppearEnd = true;
        if (m_nLifeTime > 0)
            m_liftTimer.start(m_nLifeTime);
        });
}

void NotificationItem::Disappear()
{
    // 防止重复触发
    if (m_bDisappearing) {
        return;
    }
    m_bDisappearing = true;

    // 获取父窗口大小
    QWidget* pParent = qobject_cast<QWidget*>(parent());
    if (!pParent) {
        emit itemRemoved(this);
        deleteLater();
        return;
    }

    QSize parentSize = pParent->size();

    // 计算滑出目标位置（与入场相反）
    QPropertyAnimation* animation = new QPropertyAnimation(this, "geometry");
    animation->setDuration(nDisappearTime);
    animation->setStartValue(QRect(pos().x(), pos().y(), width(), height()));

    int targetX = pos().x();

    if (m_enPos == NotifyPosition::Pos_Top_Right)
    {
        // 从右侧滑入的，向右滑出
        targetX = parentSize.width();
    }
    else if (m_enPos == NotifyPosition::Pos_Top_Left)
    {
        // 从左侧滑入的，向左滑出
        targetX = -width();
    }
    else if (m_enPos == NotifyPosition::Pos_Bottom_Left)
    {
        // 从左下滑入的，向左滑出
        targetX = -width();
    }
    else  // Pos_Bottom_Right
    {
        // 从右下滑入的，向右滑出
        targetX = parentSize.width();
    }

    animation->setEndValue(QRect(targetX, pos().y(), width(), height()));

    // 使用与入场相同的缓动效果
    QEasingCurve curve;
    curve.setType(QEasingCurve::OutCubic);
    animation->setEasingCurve(curve);

    animation->start(QAbstractAnimation::DeleteWhenStopped);

    connect(animation, &QPropertyAnimation::finished, this, [this]() {
        emit itemRemoved(this);
        deleteLater();
        });
}

void NotificationItem::paintEvent(QPaintEvent* event)
{
    QStyleOption opt;
    opt.initFrom(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);

    QWidget::paintEvent(event);
}

//
NotificationLabel::NotificationLabel(QWidget* parent,
    int nFixedWidth,
    QString content) : QWidget(parent)
{
    setObjectName(QStringLiteral("motification_label"));
    setFixedWidth(nFixedWidth);
    m_strText = content;
    m_nHeight = 0;
    setStyleSheet(QStringLiteral("QWidget#motification_label{background-color:white;border:none;}"));
}

NotificationLabel::~NotificationLabel()
{

}

void NotificationLabel::Adjust()
{
    m_strList.clear();
    QFontMetrics fm(font());
    int tpHeight = fm.height();
    int size = m_strText.length();
    QString strTp;
    for (int i = 0; i < size; i++)
    {
        strTp.append(m_strText.at(i));
        int tpWidth = fm.width(strTp);
        if (tpWidth > width())
        {
            i--;
            strTp.chop(1);
            m_strList.push_back(strTp);
            strTp.clear();
            m_nHeight += tpHeight;
        }
        else
        {
            if (i == size - 1)
            {
                m_strList.push_back(strTp);
                strTp.clear();
                m_nHeight += tpHeight;
            }
        }
    }
    setFixedHeight(m_nHeight + tpHeight / 2);
    repaint();
}

void NotificationLabel::paintEvent(QPaintEvent* event)
{
    QStyleOption opt;
    opt.initFrom(this);
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QFontMetrics fm(font());
    int tpHeight = fm.height();
    int height = tpHeight;
    p.setPen(m_conetentColor);
    for (int i = 0; i < m_strList.count(); i++)
    {
        p.drawText(QPoint(0, height), m_strList[i]);
        height += (tpHeight + m_nMargin);
    }
    QWidget::paintEvent(event);
}